<?php
/**
 * Logger
 * 
 * 作者:  姚刚 (yaogang@the9sense.com)
 * 创建时间: 2011-02-24 00:21:03
 * 修改记录: 
 * 
 * $Id: Logger.php 7 2011-03-16 05:07:39Z coosuu@gmail.com $
 */
class Logger {

    /**
     * 错误级别
     */
    const LOGGER_LEVEL_TRACE = 1;
    const LOGGER_LEVEL_DEBUG = 2;
    const LOGGER_LEVEL_WARNING = 3;
    const LOGGER_LEVEL_ERROR = 4;
    const LOGGER_LEVEL_FATAL = 5;

    /**
     * crlf
     * 换行符
     *
     * @var string
     */
    public static $crlf = PHP_EOL;

    /**
     * writeln
     *
     * @param  mixed $fpath
     * @param  mixed $message
     * @access public
     * @return void
     */
    static function writeln($fpath, $message) {

        $dir = Config::get("logger.output_dir");
        $dir = rtrim($dir, '/') . '/' . ltrim($fpath, '/');

        // 自动创建不存在的目录
        $directory = dirname($dir);
        if (!is_dir($directory)) {
            mkdir($directory, 0777, true);
        }

        chmod($directory, 0777);

        if (file_exists($dir)) {
            chmod($dir, 0777);
        }

        $fd = fopen($dir, "a+");
        if (!$fd) {
            error_log("Logger: Cannot open file ($dir)");
            return false;
        }

        fwrite($fd, "[" . date('Y-m-d H:i:s') . "]\t" . $message . self::$crlf);
        fclose($fd);
    }

    /**
     * debug
     *
     * @param  mixed $message 
     * @access public
     * @return void
     */
    function debug($message){
        return self::log($message, self::LOGGER_LEVEL_DEBUG);
    }

    /**
     * warning
     *
     * @param  mixed $message 
     * @access public
     * @return void
     */
    function warning($message){
        return self::log($message, self::LOGGER_LEVEL_WARNING);
    }

    /**
     * trace
     *
     * @param  mixed $message 
     * @access public
     * @return void
     */
    function trace($message){
        return self::log($message, self::LOGGER_LEVEL_TRACE);
    }

    /**
     * fatal
     *
     * @param  mixed $message 
     * @access public
     * @return void
     */
    function fatal($message){
        return self::log($message, self::LOGGER_LEVEL_FATAL);
    }

    /**
     * error
     *
     * @param  string $message
     * @access public
     * @return void
     */
    function error($message){
        return self::log($message, self::LOGGER_LEVEL_ERROR);
    }

    /**
     * log
     *
     * @param  mixed $message 
     * @param  mixed $level 
     * @access public
     * @return void
     */
    function log($message, $level){

        if (is_a($message, 'Exception')) {
            $message = $message->getMessage();
        }
        $backtrace = debug_backtrace();
        array_shift($backtrace); // myself
        array_shift($backtrace); //
        $traceinfo = array_shift($backtrace);
        $traceinfo['className'] = $traceinfo['class'];

        $conf = Config::get("logger.$traceinfo[className]");
        $conf['level']  = is_array($conf) && array_key_exists('level', $conf) ? $conf["level"] : Config::get("logger.level");
        $conf['output'] = is_array($conf) && array_key_exists('output', $conf) ? $conf["output"] : Config::get("logger.output");
        $conf['output_dir'] = is_array($conf) && array_key_exists('output_dir', $conf) ? $conf["output_dir"] : Config::get("logger.output_dir");
        $conf['use_arg'] = is_array($conf) && array_key_exists('use_arg', $conf) ? $conf["use_arg"] : Config::get("logger.use_arg");

        switch($level) {
            case self::LOGGER_LEVEL_TRACE:
                $myLevel = 'TRACE';
                break;
            case self::LOGGER_LEVEL_DEBUG:
                $myLevel = 'DEBUG';
                break;
            case self::LOGGER_LEVEL_WARNING:
                $myLevel = 'WARNING';
                break;
            case self::LOGGER_LEVEL_ERROR:
                $myLevel = 'ERROR';
                break;
            case self::LOGGER_LEVEL_FATAL:
                $myLevel = 'FATAL';
                break;
            default:
                $myLevel = 'N/A';
        }

        if ($conf['level'] <= $level){
            if ($traceinfo['className']){
                $out = sprintf ("[%s] [%s::%s %d] [%s] %s" . self::$crlf, date('Y-m-d H:i:s'), $traceinfo['className'], $traceinfo['function'],  $traceinfo['line'], $myLevel,  $message);
                if ($conf['output'] == 'stdout') {
                    echo $out;
                } else if ($conf['output'] == 'file') {
                    $outfile = "$conf[output_dir]/$traceinfo[className]";

                    $arg1 = null;
                    if (array_key_exists('argv', $_SERVER)) {
                        $arg1 = trim($_SERVER['argv'][1]);
                    }

                    if ($conf['use_arg'] && $arg1 && !$_SERVER['SERVER_SOFTWARE']) { // fix: 浏览器有get参数时创建怪异的文件名
                        $outfile .=  "_$arg1";
                    }
                    $outfile .= '.' . date('Y-m-d') . '.log';
                    if ($fd = @fopen($outfile, 'a+')) {
                        fwrite($fd, $out);
                        fclose($fd);
                    } else {
                        error_log("Logger: Cannot open file ($outfile)");
                    }
                } else {
                    error_log("Logger: unrecognized output type ($conf[output])");
                }
            } else if ($traceinfo['function']){
                printf ("[%s] [%s %d] %s" . self::$crlf, date('Y-m-d H:i:s'), $traceinfo['function'],  $traceinfo['line'],  $message);
            } else {
                printf ("[%s] [main] %s" . self::$crlf, date('Y-m-d H:i:s'), $message);
            }
        }
        return $outfile;
    }

    /**
     * setLevel
     *
     * @param  mixed $level
     * @access public
     * @return void
     */
    function setLevel($level){
        $logger =& logger::getInstance();
        $logger->level = $level;
    }

    /**
     * setLog
     *
     * @param  string $tmp
     * @access public
     * @return void
     */
    function setLog($tmp = '') {
        static $name;
        $tmp && $name = $tmp;
        return $name;
    }

    /**
     * &getInstance
     *
     * @access public
     * @return void
     */
    function &getInstance(){
        static $instance;
        if (!$instance){
            $instance = new Logger();
        }
        return $instance;
    }

}
